/*
 * EcmaScriptVariableVisitor.java - ScriptME
 * 
 * Copyright (c) 2009 Cesar Henriques <cesar at alttab.com.ar>.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Based on FESI Project
 * 
 * Contributors:
 * 	Jean-Marc Lugrin - initial API and implementation
 * 	Cesar Henriques <cesar at alttab.com.ar> - J2ME Porting and Extensions
 */
package org.scriptme.ecmascript.interpreter;

import java.util.Vector;

import org.scriptme.ecmascript.ast.ASTAllocationExpression;
import org.scriptme.ecmascript.ast.ASTAndExpressionSequence;
import org.scriptme.ecmascript.ast.ASTAssignmentExpression;
import org.scriptme.ecmascript.ast.ASTBinaryExpressionSequence;
import org.scriptme.ecmascript.ast.ASTBreakStatement;
import org.scriptme.ecmascript.ast.ASTCompositeReference;
import org.scriptme.ecmascript.ast.ASTConditionalExpression;
import org.scriptme.ecmascript.ast.ASTContinueStatement;
import org.scriptme.ecmascript.ast.ASTEmptyExpression;
import org.scriptme.ecmascript.ast.ASTExpressionList;
import org.scriptme.ecmascript.ast.ASTForInStatement;
import org.scriptme.ecmascript.ast.ASTForStatement;
import org.scriptme.ecmascript.ast.ASTForVarInStatement;
import org.scriptme.ecmascript.ast.ASTForVarStatement;
import org.scriptme.ecmascript.ast.ASTFormalParameterList;
import org.scriptme.ecmascript.ast.ASTFunctionCallParameters;
import org.scriptme.ecmascript.ast.ASTFunctionDeclaration;
import org.scriptme.ecmascript.ast.ASTIdentifier;
import org.scriptme.ecmascript.ast.ASTIfStatement;
import org.scriptme.ecmascript.ast.ASTLiteral;
import org.scriptme.ecmascript.ast.ASTOperator;
import org.scriptme.ecmascript.ast.ASTOrExpressionSequence;
import org.scriptme.ecmascript.ast.ASTPostfixExpression;
import org.scriptme.ecmascript.ast.ASTProgram;
import org.scriptme.ecmascript.ast.ASTPropertyIdentifierReference;
import org.scriptme.ecmascript.ast.ASTPropertyValueReference;
import org.scriptme.ecmascript.ast.ASTReturnStatement;
import org.scriptme.ecmascript.ast.ASTStatement;
import org.scriptme.ecmascript.ast.ASTStatementList;
import org.scriptme.ecmascript.ast.ASTThisReference;
import org.scriptme.ecmascript.ast.ASTUnaryExpression;
import org.scriptme.ecmascript.ast.ASTVariableDeclaration;
import org.scriptme.ecmascript.ast.ASTWhileStatement;
import org.scriptme.ecmascript.ast.ASTWithStatement;
import org.scriptme.ecmascript.ast.EcmaScriptVisitor;
import org.scriptme.ecmascript.ast.SimpleNode;
import org.scriptme.ecmascript.exceptions.ProgrammingError;
import org.scriptme.ecmascript.parser.EcmaScriptConstants;

/**
 * The variable visitor use the visitor pattern to iterate the parsed code. It
 * examine all variable declaration at the current level (it does not recurse in
 * functions) and return the list of variables as a vector.
 * <P>
 * The variable declarations will be ignored by the evaluation visitor (the tree
 * is not modified).
 */
public class EcmaScriptVariableVisitor implements EcmaScriptVisitor,
		EcmaScriptConstants {

	// The visitor work on behalf on an evaluator
	/** The evaluator. */
	private Evaluator evaluator = null;

	/** The debug. */
	private boolean debug = false;

	/** The variable list. */
	private Vector variableList = null;

	/**
	 * Create a new visitor.
	 * 
	 * @param evaluator
	 *            On behalf of this evaluator
	 */
	public EcmaScriptVariableVisitor(Evaluator evaluator) {
		super();
		this.evaluator = evaluator;
	}

	/**
	 * Process all variable declarations at the global level.
	 * 
	 * @param node
	 *            The parse tree
	 * @param evaluationSource
	 *            the evaluation source
	 * 
	 * @return A vector of variables
	 * 
	 * @evaluationSource A description of the source for error messages
	 */
	public Vector processVariableDeclarations(ASTProgram node,
			EvaluationSource evaluationSource) {
		if (debug)
			System.out.println("processVariableDeclarations for program: "
					+ node);
		variableList = new Vector();
		node.jjtAccept(this, evaluationSource);
		return variableList;
	}

	/**
	 * Process all variable declarations at the statement list level.
	 * 
	 * @param node
	 *            The parse tree
	 * @param evaluationSource
	 *            the evaluation source
	 * 
	 * @return A vector of variables
	 * 
	 * @evaluationSource A description of the source for error messages
	 */
	public Vector processVariableDeclarations(ASTStatementList node,
			EvaluationSource evaluationSource) {
		if (debug)
			System.out
					.println("processVariableDeclarations for function body: "
							+ node);
		variableList = new Vector();
		node.jjtAccept(this, evaluationSource);
		return variableList;
	}

	/*--------------------------------------------------------------------
	 * The following routines implement the walking process
	 * Irrelevant parts of the tree are skipped
	 *------------------------------------------------------------------*/

	/**
	 * Bad ast.
	 */
	private void badAST() {
		throw new ProgrammingError("Bad AST walk in EcmaScriptVariableVisitor");
	}

	// The dispatching is by node type - if the specific visitor
	// is not implemented, then this routine is called
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.SimpleNode,
	 *      java.lang.Object)
	 */
	public Object visit(SimpleNode node, Object data) {
		badAST();
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTProgram,
	 *      java.lang.Object)
	 */
	public Object visit(ASTProgram node, Object data) {
		data = node.childrenAccept(this, data);
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTStatementList,
	 *      java.lang.Object)
	 */
	public Object visit(ASTStatementList node, Object data) {
		data = node.childrenAccept(this, data);
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTFunctionDeclaration,
	 *      java.lang.Object)
	 */
	public Object visit(ASTFunctionDeclaration node, Object data) {
		; // ignore function declarations in this mode
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTFormalParameterList,
	 *      java.lang.Object)
	 */
	public Object visit(ASTFormalParameterList node, Object data) {
		badAST();
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTStatement,
	 *      java.lang.Object)
	 */
	public Object visit(ASTStatement node, Object data) {
		data = node.childrenAccept(this, data);
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTVariableDeclaration,
	 *      java.lang.Object)
	 */
	public Object visit(ASTVariableDeclaration node, Object data) {
		int nChildren = node.jjtGetNumChildren();
		if (nChildren < 1 || nChildren > 2) {
			throw new ProgrammingError("Bad AST in variable declaration");
		}
		ASTIdentifier idNode = (ASTIdentifier) (node.jjtGetChild(0));
		if (debug)
			System.out.println("VAR DECL: " + idNode.getName());
		variableList.addElement(idNode.getName());
		// try {
		// evaluator.createVariable(idNode.getName());
		// } catch (EcmaScriptException e) {
		// e.printStackTrace();
		// throw new ProgrammingError(e.getMessage());
		// }
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTIfStatement,
	 *      java.lang.Object)
	 */
	public Object visit(ASTIfStatement node, Object data) {
		data = node.childrenAccept(this, data);
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTContinueStatement,
	 *      java.lang.Object)
	 */
	public Object visit(ASTContinueStatement node, Object data) {
		; // no internal variable declarations possible
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTWhileStatement,
	 *      java.lang.Object)
	 */
	public Object visit(ASTWhileStatement node, Object data) {
		data = node.childrenAccept(this, data);
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTForStatement,
	 *      java.lang.Object)
	 */
	public Object visit(ASTForStatement node, Object data) {
		data = node.childrenAccept(this, data);
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTForInStatement,
	 *      java.lang.Object)
	 */
	public Object visit(ASTForInStatement node, Object data) {
		data = node.childrenAccept(this, data);
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTForVarStatement,
	 *      java.lang.Object)
	 */
	public Object visit(ASTForVarStatement node, Object data) {
		data = node.childrenAccept(this, data);
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTForVarInStatement,
	 *      java.lang.Object)
	 */
	public Object visit(ASTForVarInStatement node, Object data) {
		data = node.childrenAccept(this, data);
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTBreakStatement,
	 *      java.lang.Object)
	 */
	public Object visit(ASTBreakStatement node, Object data) {
		; // no internal variable declarations possible
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTReturnStatement,
	 *      java.lang.Object)
	 */
	public Object visit(ASTReturnStatement node, Object data) {
		; // no internal variable declarations possible
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTWithStatement,
	 *      java.lang.Object)
	 */
	public Object visit(ASTWithStatement node, Object data) {
		node.setEvaluationSource(data);
		data = node.childrenAccept(this, data);
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTThisReference,
	 *      java.lang.Object)
	 */
	public Object visit(ASTThisReference node, Object data) {
		; // no internal variable declarations possible
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTCompositeReference,
	 *      java.lang.Object)
	 */
	public Object visit(ASTCompositeReference node, Object data) {
		; // no internal variable declarations possible
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTFunctionCallParameters,
	 *      java.lang.Object)
	 */
	public Object visit(ASTFunctionCallParameters node, Object data) {
		; // no internal variable declarations possible
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTPropertyValueReference,
	 *      java.lang.Object)
	 */
	public Object visit(ASTPropertyValueReference node, Object data) {
		; // no internal variable declarations possible
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTPropertyIdentifierReference,
	 *      java.lang.Object)
	 */
	public Object visit(ASTPropertyIdentifierReference node, Object data) {
		; // no internal variable declarations possible
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTAllocationExpression,
	 *      java.lang.Object)
	 */
	public Object visit(ASTAllocationExpression node, Object data) {
		; // no internal variable declarations possible
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTOperator,
	 *      java.lang.Object)
	 */
	public Object visit(ASTOperator node, Object data) {
		badAST();
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTPostfixExpression,
	 *      java.lang.Object)
	 */
	public Object visit(ASTPostfixExpression node, Object data) {
		; // no internal variable declarations possible
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTUnaryExpression,
	 *      java.lang.Object)
	 */
	public Object visit(ASTUnaryExpression node, Object data) {
		; // no internal variable declarations possible
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTBinaryExpressionSequence,
	 *      java.lang.Object)
	 */
	public Object visit(ASTBinaryExpressionSequence node, Object data) {
		; // no internal variable declarations possible
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTAndExpressionSequence,
	 *      java.lang.Object)
	 */
	public Object visit(ASTAndExpressionSequence node, Object data) {
		; // no internal variable declarations possible
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTOrExpressionSequence,
	 *      java.lang.Object)
	 */
	public Object visit(ASTOrExpressionSequence node, Object data) {
		; // no internal variable declarations possible
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTConditionalExpression,
	 *      java.lang.Object)
	 */
	public Object visit(ASTConditionalExpression node, Object data) {
		; // no internal variable declarations possible
		return data;
	}

	// Can we really have a cascade ?
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTAssignmentExpression,
	 *      java.lang.Object)
	 */
	public Object visit(ASTAssignmentExpression node, Object data) {
		; // no internal variable declarations possible
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTExpressionList,
	 *      java.lang.Object)
	 */
	public Object visit(ASTExpressionList node, Object data) {
		; // no internal variable declarations possible
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTEmptyExpression,
	 *      java.lang.Object)
	 */
	public Object visit(ASTEmptyExpression node, Object data) {
		; // no internal variable declarations possible
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTLiteral,
	 *      java.lang.Object)
	 */
	public Object visit(ASTLiteral node, Object data) {
		; // no internal variable declarations possible
		return data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ast.EcmaScriptVisitor#visit(org.scriptme.ecmascript.ast.ASTIdentifier,
	 *      java.lang.Object)
	 */
	public Object visit(ASTIdentifier node, Object data) {
		; // no internal variable declarations possible
		return data;
	}
}
